// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>

#include "src/compiler/zone-stats.h"

namespace v8 {
namespace internal {
    namespace compiler {

        ZoneStats::StatsScope::StatsScope(ZoneStats* zone_stats)
            : zone_stats_(zone_stats)
            , total_allocated_bytes_at_start_(zone_stats->GetTotalAllocatedBytes())
            , max_allocated_bytes_(0)
        {
            zone_stats_->stats_.push_back(this);
            for (Zone* zone : zone_stats_->zones_) {
                size_t size = static_cast<size_t>(zone->allocation_size());
                std::pair<InitialValues::iterator, bool> res = initial_values_.insert(std::make_pair(zone, size));
                USE(res);
                DCHECK(res.second);
            }
        }

        ZoneStats::StatsScope::~StatsScope()
        {
            DCHECK_EQ(zone_stats_->stats_.back(), this);
            zone_stats_->stats_.pop_back();
        }

        size_t ZoneStats::StatsScope::GetMaxAllocatedBytes()
        {
            return std::max(max_allocated_bytes_, GetCurrentAllocatedBytes());
        }

        size_t ZoneStats::StatsScope::GetCurrentAllocatedBytes()
        {
            size_t total = 0;
            for (Zone* zone : zone_stats_->zones_) {
                total += static_cast<size_t>(zone->allocation_size());
                // Adjust for initial values.
                InitialValues::iterator it = initial_values_.find(zone);
                if (it != initial_values_.end()) {
                    total -= it->second;
                }
            }
            return total;
        }

        size_t ZoneStats::StatsScope::GetTotalAllocatedBytes()
        {
            return zone_stats_->GetTotalAllocatedBytes() - total_allocated_bytes_at_start_;
        }

        void ZoneStats::StatsScope::ZoneReturned(Zone* zone)
        {
            size_t current_total = GetCurrentAllocatedBytes();
            // Update max.
            max_allocated_bytes_ = std::max(max_allocated_bytes_, current_total);
            // Drop zone from initial value map.
            InitialValues::iterator it = initial_values_.find(zone);
            if (it != initial_values_.end()) {
                initial_values_.erase(it);
            }
        }

        ZoneStats::ZoneStats(AccountingAllocator* allocator)
            : max_allocated_bytes_(0)
            , total_deleted_bytes_(0)
            , allocator_(allocator)
        {
        }

        ZoneStats::~ZoneStats()
        {
            DCHECK(zones_.empty());
            DCHECK(stats_.empty());
        }

        size_t ZoneStats::GetMaxAllocatedBytes() const
        {
            return std::max(max_allocated_bytes_, GetCurrentAllocatedBytes());
        }

        size_t ZoneStats::GetCurrentAllocatedBytes() const
        {
            size_t total = 0;
            for (Zone* zone : zones_) {
                total += static_cast<size_t>(zone->allocation_size());
            }
            return total;
        }

        size_t ZoneStats::GetTotalAllocatedBytes() const
        {
            return total_deleted_bytes_ + GetCurrentAllocatedBytes();
        }

        Zone* ZoneStats::NewEmptyZone(const char* zone_name)
        {
            Zone* zone = new Zone(allocator_, zone_name);
            zones_.push_back(zone);
            return zone;
        }

        void ZoneStats::ReturnZone(Zone* zone)
        {
            size_t current_total = GetCurrentAllocatedBytes();
            // Update max.
            max_allocated_bytes_ = std::max(max_allocated_bytes_, current_total);
            // Update stats.
            for (StatsScope* stat_scope : stats_) {
                stat_scope->ZoneReturned(zone);
            }
            // Remove from used.
            Zones::iterator it = std::find(zones_.begin(), zones_.end(), zone);
            DCHECK(it != zones_.end());
            zones_.erase(it);
            total_deleted_bytes_ += static_cast<size_t>(zone->allocation_size());
            delete zone;
        }

    } // namespace compiler
} // namespace internal
} // namespace v8
